//
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#ifndef ZETASQL_ANALYZER_FUNCTION_SIGNATURE_MATCHER_H_
#define ZETASQL_ANALYZER_FUNCTION_SIGNATURE_MATCHER_H_

#include <functional>
#include <memory>
#include <vector>

#include "zetasql/parser/parse_tree.h"
#include "zetasql/public/coercer.h"
#include "zetasql/public/function_signature.h"
#include "zetasql/public/id_string.h"
#include "zetasql/public/input_argument_type.h"
#include "zetasql/public/language_options.h"
#include "zetasql/public/signature_match_result.h"
#include "zetasql/public/types/type.h"
#include "zetasql/public/types/type_factory.h"
#include "zetasql/resolved_ast/resolved_ast.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/span.h"

namespace zetasql {

// Function argument override generated by SignatureMatches
struct FunctionArgumentOverride {
  // Index of the argument to be overridden.
  int index;
  // New argument used to override the original argument at index.
  // Currently only lambda or sequence.
  std::unique_ptr<const ResolvedFunctionArgument> argument;
};

// Contains information about the resolution of a particular argument
// in a funtion call for a particular signature.
// This is generally passed as a vector<ArgIndexEntry>. This will generally
// be some normalized list ~1:1 with actual function call arguments, but
// may be reordered (in the case of named arguments) or extended in the
// case of optional arguments.
struct ArgIndexEntry {
  // The argument index into the function signature.
  // The value always falls into the valid range (i.e., >= 0 && <
  // function_signature.arguments().size()).
  int signature_arg_index;

  // The argument index to the function call.
  // The value either falls into the valid range (i.e., >= 0 && <
  // arg_locations.size()) or is -1 if the argument at <signature_arg_index>
  // in the signature is not provided in the current call.
  int call_arg_index;

  // The index in the concrete signature. This will usually be the same
  // as signature_arg_index, except when a repeated arbitrary is present,
  // this will cause additional arguments to be in the concrete signature
  // due to heterogeneous types per argument.
  // This may be unset during the early stages of signature matching.
  int concrete_signature_arg_index = -1;
};

// Type of callback used by the <FunctionSignatureMatches> to resolve lambda.
// See <CheckResolveLambdaTypeAndCollectTemplatedArguments> for details.
using ResolveLambdaCallback = std::function<absl::Status(
    const ASTLambda* ast_lambda, absl::Span<const IdString> arg_names,
    absl::Span<const Type* const> arg_types, const Type* body_result_type,
    bool allow_argument_coercion,
    std::unique_ptr<const ResolvedInlineLambda>* resolved_expr_out)>;

// Determines if the function signature matches the argument list, returning
// a non-templated signature if true. Exposes internal errors with the returned
// Status. If <allow_argument_coercion> is TRUE then function arguments can be
// coerced to the required signature type(s), otherwise they must be an exact
// match.
//
// <arg_ast_nodes> are the list of parser ASTNodes for each input_arguments.
// It's used to assist lambda and sequence argument resolving.
// There may be more <input_arguments> than <arg_ast_nodes> when default
// argument values have been added.
// <resolve_lambda_callback> is called to resolve lambda arguments if any. It
// can be set to nullptr if no lambda argument is expected.
// The resolved lambda arguments, if any, are put into <arg_overrides> if the
// signature matches. It's undefined otherwise.
// <arg_index_mapping> if non-null will be updated setting
// .concrete_signature_arg_index.
absl::StatusOr<bool> FunctionSignatureMatchesWithStatus(
    const LanguageOptions& language_options, const Coercer& coercer,
    const std::vector<const ASTNode*>& arg_ast_nodes,
    absl::Span<const InputArgumentType> input_arguments,
    const FunctionSignature& signature, bool allow_argument_coercion,
    TypeFactory* type_factory,
    const ResolveLambdaCallback* resolve_lambda_callback,
    std::unique_ptr<FunctionSignature>* concrete_result_signature,
    SignatureMatchResult* signature_match_result,
    std::vector<ArgIndexEntry>* arg_index_mapping,
    std::vector<FunctionArgumentOverride>* arg_overrides);

// Determines if the argument list count matches signature, returning the number
// of times each repeated argument repeats and the number of optional arguments
// present if true.
bool SignatureArgumentCountMatches(
    const FunctionSignature& signature,
    absl::Span<const InputArgumentType> input_arguments, int* repetitions,
    int* optionals, SignatureMatchResult* signature_match_result);

}  // namespace zetasql

#endif  // ZETASQL_ANALYZER_FUNCTION_SIGNATURE_MATCHER_H_
